EXERCISE:

Apply PDP to the regression example of predicting bike rentals. Fit a random forest approximation for the prediction of bike rentals (cnt). Use the partial dependence plot to visualize the relationships the model learned. Use the slides shown in class as model.

QUESTION:

Analyse the influence of days since 2011, temperature, humidity and wind speed on the predicted bike counts.

library(dplyr)
library(plotly)
library(reshape2)
library(lubridate)
library(randomForestSRC)
library(modeldata)
library(caret)
library(dplyr)
library(ggplot2)
library(lime)

set.seed(1)

days <- read.csv("day.csv")
hour <- read.csv("hour.csv")

days$dteday <- as_date(days$dteday)
days_since <- select(days, workingday, holiday, temp, hum, windspeed, cnt)
days_since$days_since_2011 <- int_length(interval(ymd("2011-01-01"), days$dteday)) / (3600*24)
days_since$SUMMER <- ifelse(days$season == 3, 1, 0)
days_since$FALL <- ifelse(days$season == 4, 1, 0)
days_since$WINTER <- ifelse(days$season == 1, 1, 0)
days_since$MISTY <- ifelse(days$weathersit == 2, 1, 0)
days_since$RAIN <- ifelse(days$weathersit == 3 | days$weathersit == 4, 1, 0)
days_since$temp <- days_since$temp * 47 - 8
days_since$hum <- days_since$hum * 100
days_since$windspeed <- days_since$windspeed * 67

rf <- rfsrc(cnt~., data=days_since)


results <- select(days_since, days_since_2011, temp, hum, windspeed, cnt)
nr <- nrow(days_since)
for(c in names(results)[1:4])
{
  for(i in 1:nr){
    r <- days_since
    r[[c]] <- days_since[[c]][i]
    sal <- predict(rf, r)$predicted
    results[[c]][i] <- (sum(sal) / nr)
  }
}

results$D1=days_since$days_since_2011
results$T1=days_since$temp
results$H1=days_since$hum
results$W1=days_since$windspeed

library(pdp)

Attaching package: ‘pdp’

The following object is masked from ‘package:randomForestSRC’:

    partial
library(vip)

Attaching package: ‘vip’

The following object is masked from ‘package:utils’:

    vi
p1=ggplot(results,aes(x=D1,y=days_since_2011))+geom_line()+geom_rug(alpha=0.1,sides='b')+labs(x='Days_since_2011')
p2=ggplot(results,aes(x=T1,y=temp))+geom_line()+geom_rug(alpha=0.1,sides='b')+labs(x='Temperature')
p3=ggplot(results,aes(x=H1,y=hum))+geom_line()+geom_rug(alpha=0.1,sides='b')+labs(x='Humidity')
p4=ggplot(results,aes(x=W1,y=windspeed))+geom_rug(alpha=0.1,sides='b')+geom_line()+labs(x='Windspeed')

subplot(p1,p2,p3,p4, shareX = FALSE, titleX = TRUE)
NA

QUESTION:

Analyse the influence of days since 2011, temperature, humidity and wind speed on the predicted bike counts.

In first place, in the PDP for *days_since_2011** we can observe that there is two differenciable parts, the first one in the first year (days 0-360 aproximately) and the second year (days 361-730 aproximately). In the first year we can observe an almost linear increase in the bicycle rentals until a few days before the day 100 that goes from 2750 to aproximately 3000. After that there is a exponencial increase that goes until the day 110 aprox fllowed by an stable period of time with a low decreasement around the day 360. In the second year we can observe a huge exponencial increasemenet that finishes the day 420, followed by a low linear increase until the day 650 and finally a decrease until the last day. In general, we can say that the bike rentals increases with the time but the last days has been a little decrease.

Secondly, in the temp plot we can observe the bike rentals depending on the temperature. At first, the bike rentals do not change in the gap including the temperatures between negative and 4 degrees and stay around 3150. In the gap between 4 and 22 degrees we can observe a huge increase in the bike renatls that gets to the maximum of almost 5200 rentals. Finally, the tempertaures above 22 degrees have less rentals each time the temperature increases until the maximum temperature around 32 degrees that have 4750 rentals. In general we can say that the renatls increases when the temperature inccreases, but when the temperatures are really high the rentals decreases slowly.

In third place, in the plot of humidity we can see that the humidities between 0 and 62,5% has very similar rentals above 4625 and after that it is produced a huge decreasement of the rentals until the humdity close to the 100% that gets to 3500 rentals. In general, the rentals decreases with the humidity except when the humidity has high values (62.5-100) and the rest of values of humidity the bike rantals are the highest but the same for all this values.

In the end, in the windspeed plot we can see that the rentals clearly decreases from 0 to 25 km/h that goes from 4625 rentals to 4000 rentals. After that, in the gap between 25 to 35 km/h we can see that the rentals do not change significantly and always is in the lowest number of rentals. In general, the rentals decrease with the wind speed.

EXERCISE:

Generate a 2D Partial Dependency Plot with humidity and temperature to predict the number of bikes rented depending of those parameters.

BE CAREFUL: due to the size, extract a set of random samples from the BBDD before generating the the data for the Partial Dependency Plot.

Show the density distribution of both input features with the 2D plot as shown in the class slides.

TIP: Use geom_tile() to generate the 2D plot. Set width and height to avoid holes.

library(tictoc)
set.seed(1)
sampled <- sample_n(days_since, 40)
temp <- sampled$temp
hum <- sampled$hum
th <- inner_join(data.frame(temp),data.frame(hum), by=character())
th$p <- 0

for(i in 1:nrow(th)){
  r <- days_since
  r[["temp"]] <- th[["temp"]][i]
  r[["hum"]] <- th[["hum"]][i]
  
  sal <- predict(rf, r)$predicted
  th[["p"]][i] <- sum(sal) / nr
}
ggplot(th, aes(x=temp, y=hum)) + geom_tile(aes(fill=p, width=10, height=10)) + labs(x="Temperature", y="Humidity") + guides(fill=guide_legend(title="Number of rentals")) + geom_rug(alpha=0.1,sides = 'b')

QUESTION:

Interpret the results.

In this plot, we can observe that the lightest the blue the highest number of rentals are produced, all of this depending on both features, temperature and humidity.

We can observe that the the lowest number of rentals are when the temperature is really low, between the -10ºC and 0ºC, and the humidty is really high, between 90 and 100%. We think it is because it’s really cold and the humidity makes it even worse. The highest rentals are produced in the days that the temperature are between 18-30 ºC and the humidity 20-70% (aprox.) We think that it is because it is a good temperature and the humidty is a good one that does not affect the weather too much. After that we can observe another zones in the plot as: 1: Temp:-10 - 0 ºC, Humidity : 70-90%
2: Temp:-10 - 0 ºC, Humidity : 20-70% 3: Temp: 0 - 11 ºC, Humidity : 90-100% 4: Temp: 0 - 11 ºC Humidity : 70-90% 5: Temp: 0 - 11 ºC, Humidity : 20-70%

6: Temp: 11 - 15 ºC, Humidity : 90-100% 7: Temp: 11 - 15 ºC, Humidity : 70-90% 8: Temp: 11 - 15 ºC, Humidity : 20-90% 9: Temp: 15 - 32 ºC, Humidity : 90-100% 10: Temp: 15 - 30 ºC, Humidity : 70-90% 11: Temp: 30 - 35 ºC, Humidity : 90-100% 12: Temp: 30 - 35 ºC, Humidity : 20-90%

We can assume that the the rentals of bikes has a positive correlation with temperature and an inverse relation with the humidity. Although when the temperature is too high it has a negative correlation.

EXERCISE:

Apply the previous concepts to predict the price of a house from the database kc_house_data.csv. In this case, use again a random forest approximation for the prediction based on the features bedrooms, bathrooms, sqft_living, sqft_lot, floors and yr_built. Use the partial dependence plot to visualize the relationships the model learned.

BE CAREFUL: due to the size, extract a set of random samples from the BBDD before generating the data for the Partial Dependency Plot.

set.seed(15)
d <- read.csv("kc_house_data.csv")

sampled <- sample_n(d, 1000)

sampled <- select(sampled, bedrooms, bathrooms, sqft_living, sqft_lot, floors, yr_built, price)

rf <- rfsrc(price~., data=sampled)
results <- select(sampled, bedrooms, bathrooms, sqft_living, floors, price)
nr <- nrow(sampled)
for(c in names(results)[1:4])
{
  for(i in 1:nr){
    r <- sampled
    r[[c]] <- sampled[[c]][i]
    sal <- predict(rf, r)$predicted
    results[[c]][i] <- sum(sal) / nr
  }
}
bedrooms1=results$bedrooms
bathrooms1=results$bathrooms
sqft_living1=results$sqft_living
floors1=results$floors

library(pdp)
library(vip)



p1=ggplot(sampled,aes(x=bedrooms,y=bedrooms1))+geom_line()+geom_rug(alpha=0.1,sides='b')+labs(x='Bedrooms')
p2=ggplot(sampled,aes(x=bathrooms,y=bathrooms1))+geom_line()+geom_rug(alpha=0.1,sides='b')+labs(x='Bathrooms')
p3=ggplot(sampled,aes(x=sqft_living,y=sqft_living1))+geom_line()+geom_rug(alpha=0.1,sides='b')+labs(x='Sqft_living')
p4=ggplot(sampled,aes(x=floors,y=floors1))+geom_line()+geom_rug(alpha=0.1,sides='b')+labs(x='Floors')

subplot(p1,p2,p3,p4, shareX = FALSE, titleX = TRUE)
NA

QUESTION:

Analyse the influence of bedrooms, bathrooms, sqft_living and floors on the predicted price.

In the Bedrooms plot, we can observe that the houses with 1 bedroom has a high price(a little over 530000) and we think that is caused by probably being in the city center and maybe they are the newst or have relly good reform. Then the 2 bedroom houses decreases the price (527500 aprox.) but with the 3 bedrooms houses has the the lowest price from all (less than 520000). We think that maybe it’s because it’s the most frequent house of all of them and it has in all zones, including the cheapes ones. After that the prices increases rapidly until higher than 530000 with 6 bedrooms houses. It is important see taht the 5 bedroom are a little cheaper than the 4 bedroom ones.

In the Bathrooms plot we can observe that has a linear relation with the price, so how many more bathrooms the price will be more. The lowest price is for 0.5 bathrooms that is around 400000 and the highest that around 4.5 bathrooms with almost 800000 asprice. The bathrooms value is a float because when a bathroom that is added to the house has no shower or other important furniture does not count as an entire bathroom an add its count as 0.5 or something like that.

In the Sqft_living plot we cn see that has a linear reltion too, if a house has more square feets the higher will be the price. The lowest is when the house has around 500 sqft and the price is a bit lower than 40000 and then increases until more than 1000000 and the sqrft is around 6770. We can observe some peaks too, and that’s maybe of the influence of the zone that the price per sqft will be more depending on this zone.

Finally, the Floors plot show us another linear relation, the price will be higher how many more rooms will have the house. We see that if the house has 1 floor the price is almost 515000, as the lowest price, and if the house has 2.5 floors has the highest price with almost 540000. But the prices from 3 and 3.5 floors are really similar to the highest. The value for floors is a float because the same reason as in bathrooms, the floors that are not comlete, as terraces are counted as 0.5 floors.

LS0tDQp0aXRsZTogIlhBSSAzOiBNb2RlbC1BZ25vc3RpYyBtZXRob2RzIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQpuYW1lczogQ2FybGVzIEN1YmVyb3MgU2F1cywgSm9yZ2UgU2FudGlhZ28gR29uesOhbGV6DQotLS0NCg0KIyMgRVhFUkNJU0U6DQoNCkFwcGx5IFBEUCB0byB0aGUgcmVncmVzc2lvbiBleGFtcGxlIG9mIHByZWRpY3RpbmcgYmlrZSByZW50YWxzLiBGaXQgYSByYW5kb20gZm9yZXN0IGFwcHJveGltYXRpb24gZm9yIHRoZSBwcmVkaWN0aW9uIG9mIGJpa2UgcmVudGFscyAoKipjbnQqKikuIFVzZSB0aGUgcGFydGlhbCBkZXBlbmRlbmNlIHBsb3QgdG8gdmlzdWFsaXplIHRoZSByZWxhdGlvbnNoaXBzIHRoZSBtb2RlbCBsZWFybmVkLiBVc2UgdGhlIHNsaWRlcyBzaG93biBpbiBjbGFzcyBhcyBtb2RlbC4gIA0KDQojIyBRVUVTVElPTjoNCg0KQW5hbHlzZSB0aGUgaW5mbHVlbmNlIG9mICoqZGF5cyBzaW5jZSAyMDExLCB0ZW1wZXJhdHVyZSwgaHVtaWRpdHkqKiBhbmQgKip3aW5kIHNwZWVkKiogb24gdGhlIHByZWRpY3RlZCBiaWtlIGNvdW50cy4NCg0KDQpgYGB7ciwgd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFfQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeShyZXNoYXBlMikNCmxpYnJhcnkobHVicmlkYXRlKQ0KbGlicmFyeShyYW5kb21Gb3Jlc3RTUkMpDQpsaWJyYXJ5KG1vZGVsZGF0YSkNCmxpYnJhcnkoY2FyZXQpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShsaW1lKQ0KDQpzZXQuc2VlZCgxKQ0KDQpkYXlzIDwtIHJlYWQuY3N2KCJkYXkuY3N2IikNCmhvdXIgPC0gcmVhZC5jc3YoImhvdXIuY3N2IikNCg0KZGF5cyRkdGVkYXkgPC0gYXNfZGF0ZShkYXlzJGR0ZWRheSkNCmRheXNfc2luY2UgPC0gc2VsZWN0KGRheXMsIHdvcmtpbmdkYXksIGhvbGlkYXksIHRlbXAsIGh1bSwgd2luZHNwZWVkLCBjbnQpDQpkYXlzX3NpbmNlJGRheXNfc2luY2VfMjAxMSA8LSBpbnRfbGVuZ3RoKGludGVydmFsKHltZCgiMjAxMS0wMS0wMSIpLCBkYXlzJGR0ZWRheSkpIC8gKDM2MDAqMjQpDQpkYXlzX3NpbmNlJFNVTU1FUiA8LSBpZmVsc2UoZGF5cyRzZWFzb24gPT0gMywgMSwgMCkNCmRheXNfc2luY2UkRkFMTCA8LSBpZmVsc2UoZGF5cyRzZWFzb24gPT0gNCwgMSwgMCkNCmRheXNfc2luY2UkV0lOVEVSIDwtIGlmZWxzZShkYXlzJHNlYXNvbiA9PSAxLCAxLCAwKQ0KZGF5c19zaW5jZSRNSVNUWSA8LSBpZmVsc2UoZGF5cyR3ZWF0aGVyc2l0ID09IDIsIDEsIDApDQpkYXlzX3NpbmNlJFJBSU4gPC0gaWZlbHNlKGRheXMkd2VhdGhlcnNpdCA9PSAzIHwgZGF5cyR3ZWF0aGVyc2l0ID09IDQsIDEsIDApDQpkYXlzX3NpbmNlJHRlbXAgPC0gZGF5c19zaW5jZSR0ZW1wICogNDcgLSA4DQpkYXlzX3NpbmNlJGh1bSA8LSBkYXlzX3NpbmNlJGh1bSAqIDEwMA0KZGF5c19zaW5jZSR3aW5kc3BlZWQgPC0gZGF5c19zaW5jZSR3aW5kc3BlZWQgKiA2Nw0KDQpyZiA8LSByZnNyYyhjbnR+LiwgZGF0YT1kYXlzX3NpbmNlKQ0KDQoNCnJlc3VsdHMgPC0gc2VsZWN0KGRheXNfc2luY2UsIGRheXNfc2luY2VfMjAxMSwgdGVtcCwgaHVtLCB3aW5kc3BlZWQsIGNudCkNCm5yIDwtIG5yb3coZGF5c19zaW5jZSkNCmZvcihjIGluIG5hbWVzKHJlc3VsdHMpWzE6NF0pDQp7DQogIGZvcihpIGluIDE6bnIpew0KICAgIHIgPC0gZGF5c19zaW5jZQ0KICAgIHJbW2NdXSA8LSBkYXlzX3NpbmNlW1tjXV1baV0NCiAgICBzYWwgPC0gcHJlZGljdChyZiwgcikkcHJlZGljdGVkDQogICAgcmVzdWx0c1tbY11dW2ldIDwtIChzdW0oc2FsKSAvIG5yKQ0KICB9DQp9DQoNCnJlc3VsdHMkRDE9ZGF5c19zaW5jZSRkYXlzX3NpbmNlXzIwMTENCnJlc3VsdHMkVDE9ZGF5c19zaW5jZSR0ZW1wDQpyZXN1bHRzJEgxPWRheXNfc2luY2UkaHVtDQpyZXN1bHRzJFcxPWRheXNfc2luY2Ukd2luZHNwZWVkDQoNCmxpYnJhcnkocGRwKQ0KbGlicmFyeSh2aXApDQoNCg0KDQpwMT1nZ3Bsb3QocmVzdWx0cyxhZXMoeD1EMSx5PWRheXNfc2luY2VfMjAxMSkpK2dlb21fbGluZSgpK2dlb21fcnVnKGFscGhhPTAuMSxzaWRlcz0nYicpK2xhYnMoeD0nRGF5c19zaW5jZV8yMDExJykNCnAyPWdncGxvdChyZXN1bHRzLGFlcyh4PVQxLHk9dGVtcCkpK2dlb21fbGluZSgpK2dlb21fcnVnKGFscGhhPTAuMSxzaWRlcz0nYicpK2xhYnMoeD0nVGVtcGVyYXR1cmUnKQ0KcDM9Z2dwbG90KHJlc3VsdHMsYWVzKHg9SDEseT1odW0pKStnZW9tX2xpbmUoKStnZW9tX3J1ZyhhbHBoYT0wLjEsc2lkZXM9J2InKStsYWJzKHg9J0h1bWlkaXR5JykNCnA0PWdncGxvdChyZXN1bHRzLGFlcyh4PVcxLHk9d2luZHNwZWVkKSkrZ2VvbV9ydWcoYWxwaGE9MC4xLHNpZGVzPSdiJykrZ2VvbV9saW5lKCkrbGFicyh4PSdXaW5kc3BlZWQnKQ0KDQpzdWJwbG90KHAxLHAyLHAzLHA0LCBzaGFyZVggPSBGQUxTRSwgdGl0bGVYID0gVFJVRSkNCg0KYGBgDQoNCiMjIFFVRVNUSU9OOg0KDQpBbmFseXNlIHRoZSBpbmZsdWVuY2Ugb2YgKipkYXlzIHNpbmNlIDIwMTEsIHRlbXBlcmF0dXJlLCBodW1pZGl0eSoqIGFuZCAqKndpbmQgc3BlZWQqKiBvbiB0aGUgcHJlZGljdGVkIGJpa2UgY291bnRzLg0KDQpJbiBmaXJzdCBwbGFjZSwgaW4gdGhlIFBEUCBmb3IgKmRheXNfc2luY2VfMjAxMSoqIHdlIGNhbiBvYnNlcnZlIHRoYXQgdGhlcmUgaXMgdHdvIGRpZmZlcmVuY2lhYmxlIHBhcnRzLCB0aGUgZmlyc3Qgb25lIGluIHRoZSBmaXJzdCB5ZWFyIChkYXlzIDAtMzYwIGFwcm94aW1hdGVseSkgYW5kIHRoZSBzZWNvbmQgeWVhciAoZGF5cyAzNjEtNzMwIGFwcm94aW1hdGVseSkuIA0KSW4gdGhlIGZpcnN0IHllYXIgd2UgY2FuIG9ic2VydmUgYW4gYWxtb3N0IGxpbmVhciBpbmNyZWFzZSBpbiB0aGUgYmljeWNsZSByZW50YWxzIHVudGlsIGEgZmV3IGRheXMgYmVmb3JlIHRoZSBkYXkgMTAwIHRoYXQgZ29lcyBmcm9tIDI3NTAgdG8gYXByb3hpbWF0ZWx5IDMwMDAuIEFmdGVyIHRoYXQgdGhlcmUgaXMgYSBleHBvbmVuY2lhbCBpbmNyZWFzZSB0aGF0IGdvZXMgdW50aWwgdGhlIGRheSAxMTAgYXByb3ggZmxsb3dlZCBieSBhbiBzdGFibGUgcGVyaW9kIG9mIHRpbWUgd2l0aCBhIGxvdyBkZWNyZWFzZW1lbnQgYXJvdW5kIHRoZSBkYXkgMzYwLg0KSW4gdGhlIHNlY29uZCB5ZWFyIHdlIGNhbiBvYnNlcnZlIGEgaHVnZSBleHBvbmVuY2lhbCBpbmNyZWFzZW1lbmV0IHRoYXQgZmluaXNoZXMgdGhlIGRheSA0MjAsIGZvbGxvd2VkIGJ5IGEgbG93IGxpbmVhciBpbmNyZWFzZSB1bnRpbCB0aGUgZGF5IDY1MCBhbmQgZmluYWxseSBhIGRlY3JlYXNlIHVudGlsIHRoZSBsYXN0IGRheS4NCkluIGdlbmVyYWwsIHdlIGNhbiBzYXkgdGhhdCB0aGUgYmlrZSByZW50YWxzIGluY3JlYXNlcyB3aXRoIHRoZSB0aW1lIGJ1dCB0aGUgbGFzdCBkYXlzIGhhcyBiZWVuIGEgbGl0dGxlIGRlY3JlYXNlLg0KDQpTZWNvbmRseSwgaW4gdGhlICoqdGVtcCoqIHBsb3Qgd2UgY2FuIG9ic2VydmUgdGhlIGJpa2UgcmVudGFscyBkZXBlbmRpbmcgb24gdGhlIHRlbXBlcmF0dXJlLiBBdCBmaXJzdCwgdGhlIGJpa2UgcmVudGFscyBkbyBub3QgY2hhbmdlIGluIHRoZSBnYXAgaW5jbHVkaW5nIHRoZSB0ZW1wZXJhdHVyZXMgYmV0d2VlbiBuZWdhdGl2ZSBhbmQgNCBkZWdyZWVzIGFuZCBzdGF5IGFyb3VuZCAzMTUwLiBJbiB0aGUgZ2FwIGJldHdlZW4gNCBhbmQgMjIgZGVncmVlcyB3ZSBjYW4gb2JzZXJ2ZSBhIGh1Z2UgaW5jcmVhc2UgaW4gdGhlIGJpa2UgcmVuYXRscyB0aGF0IGdldHMgdG8gdGhlIG1heGltdW0gb2YgYWxtb3N0IDUyMDAgcmVudGFscy4gRmluYWxseSwgdGhlIHRlbXBlcnRhdXJlcyBhYm92ZSAyMiBkZWdyZWVzIGhhdmUgbGVzcyByZW50YWxzIGVhY2ggdGltZSB0aGUgdGVtcGVyYXR1cmUgaW5jcmVhc2VzIHVudGlsIHRoZSBtYXhpbXVtIHRlbXBlcmF0dXJlIGFyb3VuZCAzMiBkZWdyZWVzIHRoYXQgaGF2ZSA0NzUwIHJlbnRhbHMuIEluIGdlbmVyYWwgd2UgY2FuIHNheSB0aGF0IHRoZSByZW5hdGxzIGluY3JlYXNlcyB3aGVuIHRoZSB0ZW1wZXJhdHVyZSBpbmNjcmVhc2VzLCBidXQgd2hlbiB0aGUgdGVtcGVyYXR1cmVzIGFyZSByZWFsbHkgaGlnaCB0aGUgcmVudGFscyBkZWNyZWFzZXMgc2xvd2x5Lg0KDQpJbiB0aGlyZCBwbGFjZSwgaW4gdGhlIHBsb3Qgb2YgKipodW1pZGl0eSoqIHdlIGNhbiBzZWUgdGhhdCB0aGUgaHVtaWRpdGllcyBiZXR3ZWVuIDAgYW5kIDYyLDUlIGhhcyB2ZXJ5IHNpbWlsYXIgcmVudGFscyBhYm92ZSA0NjI1IGFuZCBhZnRlciB0aGF0IGl0IGlzIHByb2R1Y2VkIGEgaHVnZSBkZWNyZWFzZW1lbnQgb2YgdGhlIHJlbnRhbHMgdW50aWwgdGhlIGh1bWRpdHkgY2xvc2UgdG8gdGhlIDEwMCUgdGhhdCBnZXRzIHRvIDM1MDAgcmVudGFscy4gSW4gZ2VuZXJhbCwgdGhlIHJlbnRhbHMgZGVjcmVhc2VzIHdpdGggdGhlIGh1bWlkaXR5IGV4Y2VwdCB3aGVuIHRoZSBodW1pZGl0eSBoYXMgaGlnaCB2YWx1ZXMgKDYyLjUtMTAwKSBhbmQgdGhlIHJlc3Qgb2YgdmFsdWVzIG9mIGh1bWlkaXR5IHRoZSBiaWtlIHJhbnRhbHMgYXJlIHRoZSBoaWdoZXN0IGJ1dCB0aGUgc2FtZSBmb3IgYWxsIHRoaXMgdmFsdWVzLg0KDQpJbiB0aGUgZW5kLCBpbiB0aGUgKip3aW5kc3BlZWQqKiBwbG90IHdlIGNhbiBzZWUgdGhhdCB0aGUgcmVudGFscyBjbGVhcmx5IGRlY3JlYXNlcyBmcm9tIDAgdG8gMjUga20vaCB0aGF0IGdvZXMgZnJvbSA0NjI1IHJlbnRhbHMgdG8gNDAwMCByZW50YWxzLiBBZnRlciB0aGF0LCBpbiB0aGUgZ2FwIGJldHdlZW4gMjUgdG8gMzUga20vaCB3ZSBjYW4gc2VlIHRoYXQgdGhlIHJlbnRhbHMgZG8gbm90IGNoYW5nZSBzaWduaWZpY2FudGx5IGFuZCBhbHdheXMgaXMgaW4gdGhlIGxvd2VzdCBudW1iZXIgb2YgcmVudGFscy4gSW4gZ2VuZXJhbCwgdGhlIHJlbnRhbHMgZGVjcmVhc2Ugd2l0aCB0aGUgd2luZCBzcGVlZC4NCg0KDQoNCg0KIyMgRVhFUkNJU0U6DQoNCkdlbmVyYXRlIGEgMkQgUGFydGlhbCBEZXBlbmRlbmN5IFBsb3Qgd2l0aCBodW1pZGl0eSBhbmQgdGVtcGVyYXR1cmUgdG8gcHJlZGljdCB0aGUgbnVtYmVyIG9mIGJpa2VzIHJlbnRlZCBkZXBlbmRpbmcgb2YgdGhvc2UgcGFyYW1ldGVycy4NCg0KQkUgQ0FSRUZVTDogZHVlIHRvIHRoZSBzaXplLCBleHRyYWN0IGEgc2V0IG9mIHJhbmRvbSBzYW1wbGVzIGZyb20gdGhlIEJCREQgYmVmb3JlIGdlbmVyYXRpbmcgdGhlIHRoZSBkYXRhIGZvciB0aGUgUGFydGlhbCBEZXBlbmRlbmN5IFBsb3QuIA0KDQpTaG93IHRoZSBkZW5zaXR5IGRpc3RyaWJ1dGlvbiBvZiBib3RoIGlucHV0IGZlYXR1cmVzIHdpdGggdGhlIDJEIHBsb3QgYXMgc2hvd24gaW4gdGhlIGNsYXNzIHNsaWRlcy4gDQoNClRJUDogVXNlIGdlb21fdGlsZSgpIHRvIGdlbmVyYXRlIHRoZSAyRCBwbG90LiBTZXQgd2lkdGggYW5kIGhlaWdodCB0byBhdm9pZCBob2xlcy4gDQoNCg0KDQoNCmBgYHtyLHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KHRpY3RvYykNCnNldC5zZWVkKDEpDQpzYW1wbGVkIDwtIHNhbXBsZV9uKGRheXNfc2luY2UsIDQwKQ0KdGVtcCA8LSBzYW1wbGVkJHRlbXANCmh1bSA8LSBzYW1wbGVkJGh1bQ0KdGggPC0gaW5uZXJfam9pbihkYXRhLmZyYW1lKHRlbXApLGRhdGEuZnJhbWUoaHVtKSwgYnk9Y2hhcmFjdGVyKCkpDQp0aCRwIDwtIDANCg0KZm9yKGkgaW4gMTpucm93KHRoKSl7DQogIHIgPC0gZGF5c19zaW5jZQ0KICByW1sidGVtcCJdXSA8LSB0aFtbInRlbXAiXV1baV0NCiAgcltbImh1bSJdXSA8LSB0aFtbImh1bSJdXVtpXQ0KICANCiAgc2FsIDwtIHByZWRpY3QocmYsIHIpJHByZWRpY3RlZA0KICB0aFtbInAiXV1baV0gPC0gc3VtKHNhbCkgLyBucg0KfQ0KZ2dwbG90KHRoLCBhZXMoeD10ZW1wLCB5PWh1bSkpICsgZ2VvbV90aWxlKGFlcyhmaWxsPXAsIHdpZHRoPTEwLCBoZWlnaHQ9MTApKSArIGxhYnMoeD0iVGVtcGVyYXR1cmUiLCB5PSJIdW1pZGl0eSIpICsgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKHRpdGxlPSJOdW1iZXIgb2YgcmVudGFscyIpKSArIGdlb21fcnVnKGFscGhhPTAuMSxzaWRlcyA9ICdiJykNCg0KYGBgDQoNCiMjIFFVRVNUSU9OOg0KDQpJbnRlcnByZXQgdGhlIHJlc3VsdHMuDQoNCg0KSW4gdGhpcyBwbG90LCB3ZSBjYW4gb2JzZXJ2ZSB0aGF0IHRoZSBsaWdodGVzdCB0aGUgYmx1ZSB0aGUgaGlnaGVzdCBudW1iZXIgb2YgcmVudGFscyBhcmUgcHJvZHVjZWQsIGFsbCBvZiB0aGlzIGRlcGVuZGluZyBvbiBib3RoIGZlYXR1cmVzLCB0ZW1wZXJhdHVyZSBhbmQgaHVtaWRpdHkuDQoNCldlIGNhbiBvYnNlcnZlIHRoYXQgdGhlIHRoZSBsb3dlc3QgbnVtYmVyIG9mIHJlbnRhbHMgYXJlIHdoZW4gdGhlIHRlbXBlcmF0dXJlIGlzIHJlYWxseSBsb3csIGJldHdlZW4gdGhlIC0xMMK6QyBhbmQgMMK6QywgYW5kIHRoZSBodW1pZHR5IGlzIHJlYWxseSBoaWdoLCBiZXR3ZWVuIDkwIGFuZCAxMDAlLiBXZSB0aGluayBpdCBpcyBiZWNhdXNlIGl0J3MgcmVhbGx5IGNvbGQgYW5kIHRoZSBodW1pZGl0eSBtYWtlcyBpdCBldmVuIHdvcnNlLg0KVGhlIGhpZ2hlc3QgcmVudGFscyBhcmUgcHJvZHVjZWQgaW4gdGhlIGRheXMgdGhhdCB0aGUgdGVtcGVyYXR1cmUgYXJlIGJldHdlZW4gMTgtMzAgwrpDIGFuZCB0aGUgaHVtaWRpdHkgMjAtNzAlIChhcHJveC4pIFdlIHRoaW5rIHRoYXQgaXQgaXMgYmVjYXVzZSBpdCBpcyBhIGdvb2QgdGVtcGVyYXR1cmUgYW5kIHRoZSBodW1pZHR5IGlzIGEgZ29vZCBvbmUgdGhhdCBkb2VzIG5vdCBhZmZlY3QgdGhlIHdlYXRoZXIgdG9vIG11Y2guDQpBZnRlciB0aGF0IHdlIGNhbiBvYnNlcnZlIGFub3RoZXIgem9uZXMgaW4gdGhlIHBsb3QgYXM6DQoxOiBUZW1wOi0xMCAtIDAgwrpDLCAgSHVtaWRpdHkgOiA3MC05MCUgIA0KMjogVGVtcDotMTAgLSAwIMK6QywgIEh1bWlkaXR5IDogMjAtNzAlIA0KMzogVGVtcDogMCAtIDExIMK6QywgIEh1bWlkaXR5IDogOTAtMTAwJSANCjQ6IFRlbXA6IDAgLSAxMSDCukMgIEh1bWlkaXR5IDogNzAtOTAlIA0KNTogVGVtcDogMCAtIDExIMK6QywgIEh1bWlkaXR5IDogMjAtNzAlICANCg0KNjogVGVtcDogMTEgLSAxNSDCukMsICBIdW1pZGl0eSA6IDkwLTEwMCUgDQo3OiBUZW1wOiAxMSAtIDE1IMK6QywgIEh1bWlkaXR5IDogNzAtOTAlIA0KODogVGVtcDogMTEgLSAxNSDCukMsICBIdW1pZGl0eSA6IDIwLTkwJSANCjk6IFRlbXA6IDE1IC0gMzIgwrpDLCAgSHVtaWRpdHkgOiA5MC0xMDAlIA0KMTA6IFRlbXA6IDE1IC0gMzAgwrpDLCAgSHVtaWRpdHkgOiA3MC05MCUgDQoxMTogVGVtcDogMzAgLSAzNSDCukMsICBIdW1pZGl0eSA6IDkwLTEwMCUgDQoxMjogVGVtcDogMzAgLSAzNSDCukMsICBIdW1pZGl0eSA6IDIwLTkwJSANCg0KDQpXZSBjYW4gYXNzdW1lIHRoYXQgdGhlIHRoZSByZW50YWxzIG9mIGJpa2VzIGhhcyBhIHBvc2l0aXZlIGNvcnJlbGF0aW9uIHdpdGggdGVtcGVyYXR1cmUgYW5kIGFuIGludmVyc2UgcmVsYXRpb24gd2l0aCB0aGUgaHVtaWRpdHkuIEFsdGhvdWdoIHdoZW4gdGhlIHRlbXBlcmF0dXJlIGlzIHRvbyBoaWdoIGl0IGhhcyBhIG5lZ2F0aXZlIGNvcnJlbGF0aW9uLg0KDQoNCiMjIEVYRVJDSVNFOg0KDQpBcHBseSB0aGUgcHJldmlvdXMgY29uY2VwdHMgdG8gcHJlZGljdCB0aGUgKipwcmljZSoqIG9mIGEgaG91c2UgZnJvbSB0aGUgZGF0YWJhc2UgKiprY19ob3VzZV9kYXRhLmNzdioqLiBJbiB0aGlzIGNhc2UsIHVzZSBhZ2FpbiBhIHJhbmRvbSBmb3Jlc3QgYXBwcm94aW1hdGlvbiBmb3IgdGhlIHByZWRpY3Rpb24gYmFzZWQgb24gdGhlIGZlYXR1cmVzICoqYmVkcm9vbXMqKiwgKipiYXRocm9vbXMqKiwgKipzcWZ0X2xpdmluZyoqLCAqKnNxZnRfbG90KiosICoqZmxvb3JzKiogYW5kICoqeXJfYnVpbHQqKi4gDQpVc2UgdGhlIHBhcnRpYWwgZGVwZW5kZW5jZSBwbG90IHRvIHZpc3VhbGl6ZSB0aGUgcmVsYXRpb25zaGlwcyB0aGUgbW9kZWwgbGVhcm5lZC4NCg0KQkUgQ0FSRUZVTDogZHVlIHRvIHRoZSBzaXplLCBleHRyYWN0IGEgc2V0IG9mIHJhbmRvbSBzYW1wbGVzIGZyb20gdGhlIEJCREQgYmVmb3JlIGdlbmVyYXRpbmcgdGhlIGRhdGEgZm9yIHRoZSBQYXJ0aWFsIERlcGVuZGVuY3kgUGxvdC4gDQoNCg0KDQoNCmBgYHtyLCB9DQpzZXQuc2VlZCgxNSkNCmQgPC0gcmVhZC5jc3YoImtjX2hvdXNlX2RhdGEuY3N2IikNCg0Kc2FtcGxlZCA8LSBzYW1wbGVfbihkLCAxMDAwKQ0KDQpzYW1wbGVkIDwtIHNlbGVjdChzYW1wbGVkLCBiZWRyb29tcywgYmF0aHJvb21zLCBzcWZ0X2xpdmluZywgc3FmdF9sb3QsIGZsb29ycywgeXJfYnVpbHQsIHByaWNlKQ0KDQpyZiA8LSByZnNyYyhwcmljZX4uLCBkYXRhPXNhbXBsZWQpDQpyZXN1bHRzIDwtIHNlbGVjdChzYW1wbGVkLCBiZWRyb29tcywgYmF0aHJvb21zLCBzcWZ0X2xpdmluZywgZmxvb3JzLCBwcmljZSkNCm5yIDwtIG5yb3coc2FtcGxlZCkNCmZvcihjIGluIG5hbWVzKHJlc3VsdHMpWzE6NF0pDQp7DQogIGZvcihpIGluIDE6bnIpew0KICAgIHIgPC0gc2FtcGxlZA0KICAgIHJbW2NdXSA8LSBzYW1wbGVkW1tjXV1baV0NCiAgICBzYWwgPC0gcHJlZGljdChyZiwgcikkcHJlZGljdGVkDQogICAgcmVzdWx0c1tbY11dW2ldIDwtIHN1bShzYWwpIC8gbnINCiAgfQ0KfQ0KYmVkcm9vbXMxPXJlc3VsdHMkYmVkcm9vbXMNCmJhdGhyb29tczE9cmVzdWx0cyRiYXRocm9vbXMNCnNxZnRfbGl2aW5nMT1yZXN1bHRzJHNxZnRfbGl2aW5nDQpmbG9vcnMxPXJlc3VsdHMkZmxvb3JzDQoNCmxpYnJhcnkocGRwKQ0KbGlicmFyeSh2aXApDQoNCg0KDQpwMT1nZ3Bsb3Qoc2FtcGxlZCxhZXMoeD1iZWRyb29tcyx5PWJlZHJvb21zMSkpK2dlb21fbGluZSgpK2dlb21fcnVnKGFscGhhPTAuMSxzaWRlcz0nYicpK2xhYnMoeD0nQmVkcm9vbXMnKQ0KcDI9Z2dwbG90KHNhbXBsZWQsYWVzKHg9YmF0aHJvb21zLHk9YmF0aHJvb21zMSkpK2dlb21fbGluZSgpK2dlb21fcnVnKGFscGhhPTAuMSxzaWRlcz0nYicpK2xhYnMoeD0nQmF0aHJvb21zJykNCnAzPWdncGxvdChzYW1wbGVkLGFlcyh4PXNxZnRfbGl2aW5nLHk9c3FmdF9saXZpbmcxKSkrZ2VvbV9saW5lKCkrZ2VvbV9ydWcoYWxwaGE9MC4xLHNpZGVzPSdiJykrbGFicyh4PSdTcWZ0X2xpdmluZycpDQpwND1nZ3Bsb3Qoc2FtcGxlZCxhZXMoeD1mbG9vcnMseT1mbG9vcnMxKSkrZ2VvbV9saW5lKCkrZ2VvbV9ydWcoYWxwaGE9MC4xLHNpZGVzPSdiJykrbGFicyh4PSdGbG9vcnMnKQ0KDQpzdWJwbG90KHAxLHAyLHAzLHA0LCBzaGFyZVggPSBGQUxTRSwgdGl0bGVYID0gVFJVRSkNCg0KYGBgDQoNCg0KIyMgUVVFU1RJT046DQoNCkFuYWx5c2UgdGhlIGluZmx1ZW5jZSBvZiAqKmJlZHJvb21zLCBiYXRocm9vbXMsIHNxZnRfbGl2aW5nKiogYW5kICoqZmxvb3JzKiogb24gdGhlIHByZWRpY3RlZCBwcmljZS4NCg0KSW4gdGhlICoqQmVkcm9vbXMqKiBwbG90LCB3ZSBjYW4gb2JzZXJ2ZSB0aGF0IHRoZSBob3VzZXMgd2l0aCAxIGJlZHJvb20gaGFzIGEgaGlnaCBwcmljZShhIGxpdHRsZSBvdmVyIDUzMDAwMCkgYW5kIHdlIHRoaW5rIHRoYXQgaXMgY2F1c2VkIGJ5IHByb2JhYmx5IGJlaW5nIGluIHRoZSBjaXR5IGNlbnRlciBhbmQgbWF5YmUgdGhleSBhcmUgdGhlIG5ld3N0IG9yIGhhdmUgcmVsbHkgZ29vZCByZWZvcm0uIFRoZW4gdGhlIDIgYmVkcm9vbSBob3VzZXMgZGVjcmVhc2VzIHRoZSBwcmljZSAoNTI3NTAwIGFwcm94LikgYnV0IHdpdGggdGhlIDMgYmVkcm9vbXMgaG91c2VzIGhhcyB0aGUgdGhlIGxvd2VzdCBwcmljZSBmcm9tIGFsbCAobGVzcyB0aGFuIDUyMDAwMCkuIFdlIHRoaW5rIHRoYXQgbWF5YmUgaXQncyBiZWNhdXNlIGl0J3MgdGhlIG1vc3QgZnJlcXVlbnQgaG91c2Ugb2YgYWxsIG9mIHRoZW0gYW5kIGl0IGhhcyBpbiBhbGwgem9uZXMsIGluY2x1ZGluZyB0aGUgY2hlYXBlcyBvbmVzLiBBZnRlciB0aGF0IHRoZSBwcmljZXMgaW5jcmVhc2VzIHJhcGlkbHkgdW50aWwgaGlnaGVyIHRoYW4gNTMwMDAwIHdpdGggNiBiZWRyb29tcyBob3VzZXMuIEl0IGlzIGltcG9ydGFudCBzZWUgdGFodCB0aGUgNSBiZWRyb29tIGFyZSBhIGxpdHRsZSBjaGVhcGVyIHRoYW4gdGhlIDQgYmVkcm9vbSBvbmVzLg0KDQpJbiB0aGUgKipCYXRocm9vbXMqKiBwbG90IHdlIGNhbiBvYnNlcnZlIHRoYXQgaGFzIGEgbGluZWFyIHJlbGF0aW9uIHdpdGggdGhlIHByaWNlLCBzbyBob3cgbWFueSBtb3JlIGJhdGhyb29tcyB0aGUgcHJpY2Ugd2lsbCBiZSBtb3JlLiBUaGUgbG93ZXN0IHByaWNlIGlzIGZvciAwLjUgYmF0aHJvb21zIHRoYXQgaXMgYXJvdW5kIDQwMDAwMCBhbmQgdGhlIGhpZ2hlc3QgdGhhdCBhcm91bmQgNC41IGJhdGhyb29tcyB3aXRoIGFsbW9zdCA4MDAwMDAgYXNwcmljZS4gVGhlIGJhdGhyb29tcyB2YWx1ZSBpcyBhIGZsb2F0IGJlY2F1c2Ugd2hlbiBhIGJhdGhyb29tIHRoYXQgaXMgYWRkZWQgdG8gdGhlIGhvdXNlIGhhcyBubyBzaG93ZXIgb3Igb3RoZXIgaW1wb3J0YW50IGZ1cm5pdHVyZSBkb2VzIG5vdCBjb3VudCBhcyBhbiBlbnRpcmUgYmF0aHJvb20gYW4gYWRkIGl0cyBjb3VudCBhcyAwLjUgb3Igc29tZXRoaW5nIGxpa2UgdGhhdC4NCg0KSW4gdGhlICoqU3FmdF9saXZpbmcqKiBwbG90IHdlIGNuIHNlZSB0aGF0IGhhcyBhIGxpbmVhciByZWx0aW9uIHRvbywgaWYgYSBob3VzZSBoYXMgbW9yZSBzcXVhcmUgZmVldHMgdGhlIGhpZ2hlciB3aWxsIGJlIHRoZSBwcmljZS4gVGhlIGxvd2VzdCBpcyB3aGVuIHRoZSBob3VzZSBoYXMgYXJvdW5kIDUwMCBzcWZ0IGFuZCB0aGUgcHJpY2UgaXMgYSBiaXQgbG93ZXIgdGhhbiA0MDAwMCBhbmQgdGhlbiBpbmNyZWFzZXMgdW50aWwgbW9yZSB0aGFuIDEwMDAwMDAgYW5kIHRoZSBzcXJmdCBpcyBhcm91bmQgNjc3MC4gV2UgY2FuIG9ic2VydmUgc29tZSBwZWFrcyB0b28sIGFuZCB0aGF0J3MgbWF5YmUgb2YgdGhlIGluZmx1ZW5jZSBvZiB0aGUgem9uZSB0aGF0IHRoZSBwcmljZSBwZXIgc3FmdCB3aWxsIGJlIG1vcmUgZGVwZW5kaW5nIG9uIHRoaXMgem9uZS4NCg0KRmluYWxseSwgdGhlICoqRmxvb3JzKiogcGxvdCBzaG93IHVzIGFub3RoZXIgbGluZWFyIHJlbGF0aW9uLCB0aGUgcHJpY2Ugd2lsbCBiZSBoaWdoZXIgaG93IG1hbnkgbW9yZSByb29tcyB3aWxsIGhhdmUgdGhlIGhvdXNlLiBXZSBzZWUgdGhhdCBpZiB0aGUgaG91c2UgaGFzIDEgZmxvb3IgdGhlIHByaWNlIGlzIGFsbW9zdCA1MTUwMDAsIGFzIHRoZSBsb3dlc3QgcHJpY2UsIGFuZCBpZiB0aGUgaG91c2UgaGFzIDIuNSBmbG9vcnMgaGFzIHRoZSBoaWdoZXN0IHByaWNlIHdpdGggYWxtb3N0IDU0MDAwMC4gQnV0IHRoZSBwcmljZXMgZnJvbSAzIGFuZCAzLjUgZmxvb3JzIGFyZSByZWFsbHkgc2ltaWxhciB0byB0aGUgaGlnaGVzdC4gVGhlIHZhbHVlIGZvciBmbG9vcnMgaXMgYSBmbG9hdCBiZWNhdXNlIHRoZSBzYW1lIHJlYXNvbiBhcyBpbiBiYXRocm9vbXMsIHRoZSBmbG9vcnMgdGhhdCBhcmUgbm90IGNvbWxldGUsIGFzIHRlcnJhY2VzIGFyZSBjb3VudGVkIGFzIDAuNSBmbG9vcnMuDQoNCg0KDQoNCg==